Obsplot(
{
"color": {"legend": True}
"grid": False,
"marks": [Plot.dot(data, {"x": "x", "y": "y", "fill": "type", "r": 5})],
}
)Usage
For a quick usage introduction, see getting started.
From JavaScript to Python
Converting a plot specification from JavaScript to Python should be straightforward most of the time:
- all dictionary keys must be quoted (so
x:becomes"x":) - JavaScript
trueandfalsemust be replaced byTrueandFalse - JavaScript
nullmust be replaced byNone
So for example, the following JavaScript code:
Plot(
{
color: {legend: true}
grid: false,
marks: [Plot.dot(data, {x: "x", y: "y", fill: "type", r: 5})]
}
)Becomes:
It is possible to directly call JavaScript methods from the Plot, d3 and Math modules. You must first import the corresponding class.
from pyobsplot import Obsplot, Plot, d3
Obsplot({
"x": {
"axis": None
},
"marks": [
Plot.lineY(d3.cumsum({ "length": 100 }, d3.randomNormal()))
]
})If your specification includes JavaScript code such as anonymous functions, you can pass it as a string by using the js method (after importing it):
from pyobsplot import Obsplot, Plot, d3, js
import polars as pl
data = pl.DataFrame(
{
"x": [1, 5, 2, 4, 6, 2, 4],
"y": [2, 1, 3, 4, 5, 1, 2],
"type": ["T1", "T2", "T1", "T2", "T1", "T1", "T2"],
}
)
Obsplot({
"grid": True,
"marks": [
Plot.dot(data, {
"x": "x", "y": "y", "r": 8,
"stroke": "red", "fill": "red",
"fillOpacity": js("d => d.type == 'T1' ? 1 : 0")
})
]
})Alternative syntaxes
As we’ve already seen, the most common syntax for a plot specification is to pass it as a dictionary :
Obsplot({
"grid": True,
"color": {"legend": True}
"marks": [
Plot.dot(data, {"x": "x", "y": "y", "fill": "type"})
],
})But it is also possible to pass top level arguments as kwargs to Obsplot, which would give the following:
Obsplot(
grid = True,
color = {"legend": True},
marks = [
Plot.dot(data, {"x": "x", "y": "y", "fill": "type"})
],
)Finally, for the simplest cases, you can also pass a mark method directly to Obsplot. The JavaScript plot() method will be called automatically to display the plot:
import random
Obsplot(
Plot.tickX(
[random.gauss(0,1) for i in range(1000)],
{"stroke": "red", "opacity": 0.2}
)
)DataFrames and Series
Pandas and polars DataFrames can be passed directly to Obsplot. They will be converted to JavaScript objects via Arrow IPC serialization, to ensure speed and data types conversion.
import polars as pl
from datetime import date
df = pl.DataFrame({
"Date": [date(2023, 1, 1), date(2023, 1, 2), date(2023, 1, 3), date(2023, 1, 4)],
"Value": [4.2, 3.8, 4.5, 4.7]
})
Obsplot({
"x": {"grid": True},
"y": {"domain": [0, 5]},
"marks": [Plot.dot(df, {"x": "Date", "y": "Value"})]
})If you pass a pandas or polars Series object, it will be automatically converted to a DataFrame with one column:
value = df.get_column("Value")
Obsplot(
Plot.tickX(value, {"x": "Value"})
)pyobsplot implements a simple caching mechanism for some data objects: it currently works for DataFrames and for GeoJson objects.
Sometimes the same data object is used several times in a plot specification, such as:
penguins = pl.read_csv("data/penguins.csv")
Obsplot({
"height": 600,
"grid": True,
"facet": {
"data": penguins,
"x": "sex",
"y": "species",
"marginRight": 80
},
"marks": [
Plot.frame({"facet": False}),
Plot.dot(penguins, {
"x": "culmen_depth_mm",
"y": "culmen_length_mm",
"r": 1.5,
"fill": "#ccc",
"facet": "exclude"
}),
Plot.dot(penguins, {
"x": "culmen_depth_mm",
"y": "culmen_length_mm",
"facet": True
})
]
})In this case, caching ensures that the penguins DataFrame is only serialized and transmitted once instead of three times.
datetime objects
datetime.date and datetime.datetime Python objects are automatically serialized and converted to JavaScript Date objects.
That makes the following two specifications equivalent:
Obsplot({
"x": {"domain": [js("new Date('2021-01-01')"), js("new Date('2022-01-01')")]},
"grid": True
})from datetime import date
Obsplot({
"x": {"domain": [date(2021,1,1), date(2022,1,1)]},
"grid": True
})As well as the two following ones, using datetime:
Obsplot({
"x": {"domain": [js("new Date('2021-01-01T07:00:00')"), js("new Date('2021-01-01T08:00:00')")]},
"grid": True
})from datetime import datetime
Obsplot({
"x": {"domain": [datetime(2021,1,1,7,0,0), datetime(2021,1,1,8,0,0)]},
"grid": True
})Interactivity
The fact that Obsplot are Jupyter widgets allow for basic interactivity. More specifically, you can set the spec attribute of an existing Obsplot to another plot specification and it will update it.
This allows to do things like the following, where a plot is updated depending on the value of a Jupyter IntSlider widget:
def generate_spec(length):
return {
"x": {
"axis": None
},
"y": {
},
"marks": [
Plot.lineY(d3.cumsum({ "length": length }, d3.randomNormal()))
]
}
def change_spec(change):
new_length = change['new']
p.spec = generate_spec(new_length)
w = IntSlider(value = 100, min = 0, max = 1000)
p = Obsplot(generate_spec(w.value))
w.observe(change_spec, names='value')
display(w)
pQuarto
Obsplot plots are compatible with quarto HTML formats: plots will be inserted as SVG or <figure> elements.
If your source document is a jupyter notebook (and not a .qmd file), then you have to use the --execute argument to force plot computation and to make them visible in the output:
quarto render test.ipynb --execute --to htmlUnfortunately, as widgets, Obsplot plots are currently not available in any other quarto formats such as PDF.